from pyjvm.instructions.common import Instruction, BytecodeReader, \
    BranchInstruction, branch
from pyjvm.runtime.thread import Frame
from pyjvm.runtime.strpool import StringPool


class GOTO(BranchInstruction):

    def execute(self, frame: Frame):
        branch(frame, self.offset)


class GOTO_W(Instruction):

    def __init__(self):
        Instruction.__init__(self)
        self.offset = None

    def fetch_operands(self, reader: BytecodeReader):
        self.offset = reader.int32()

    def execute(self, frame: Frame):
        branch(frame, self.offset)


class JSR(Instruction):

    def execute(self, frame: Frame):
        pass


class TABLE_SWITCH(Instruction):

    def __init__(self):
        Instruction.__init__(self)
        self.default_offset = None
        self.low = None
        self.high = None
        self.jump_offset_sets = None

    def fetch_operands(self, reader: BytecodeReader):
        reader.skip_padding()
        self.default_offset = reader.int32()
        self.low = reader.int32()
        self.high = reader.int32()
        jump_offset_count = self.high - self.low + 1
        self.jump_offset_sets = reader.int32s(jump_offset_count)

    def execute(self, frame: Frame):
        index = frame.operand_stack.pop()
        if self.low <= index <= self.high:
            offset = self.jump_offset_sets[index - self.low]
        else:
            offset = self.default_offset
        branch(frame, offset)


class LOOKUP_SWITCH(Instruction):

    def __init__(self):
        Instruction.__init__(self)
        self.default_offset = None
        self.npairs = None
        self.match_offsets = None

    def fetch_operands(self, reader: BytecodeReader):
        reader.skip_padding()
        self.default_offset = reader.int32()
        self.npairs = reader.int32()
        self.match_offsets = reader.int32s(self.npairs * 2)

    def execute(self, frame: Frame):
        key = frame.operand_stack.pop()
        for i in range(0, self.npairs * 2, 2):
            if self.match_offsets[i] == key:
                offset = self.match_offsets[i + 1]
                branch(frame, offset)
                return
        branch(frame, self.default_offset)


class RETURN(Instruction):

    def execute(self, frame: Frame):
        cur_frame = frame.thread.pop_frame()
        method = cur_frame.method
        if method.name == '<init>' and method.klass.name == 'java/lang/String':
            StringPool.intern(cur_frame.local_vars[0])


class XRETURN(Instruction):

    def execute(self, frame: Frame):
        thread = frame.thread
        cur_frame = thread.pop_frame()
        invoker_frame = thread.top_frame()
        val = cur_frame.operand_stack.pop()
        invoker_frame.operand_stack.push(val)


class ARETURN(XRETURN):
    pass


class FRETURN(XRETURN):
    pass


class DRETURN(XRETURN):
    pass


class IRETURN(XRETURN):
    pass


class LRETURN(XRETURN):
    pass


class RET(Instruction):
    pass  # todo
